﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Vds2465Parser
{
	internal static class GenParse_UserData_30_RepType01Numbers
	{
		// TODO: the current algorithm is wrong check and correct the calculation
		// 1024.25 can not be generated and parsed back correctly

		#region constants

		private const byte Multiplicator = 4;

		#endregion constants

		#region internal methods

		internal static double Parse_RepType01_Bytes(byte lowByte, byte highByte)
		{
			double measurementValue = 0.0;

			// the number is negativ if the highest bit 7 is set
			bool negativeNumber = highByte >= 0x80;

			// get exponent value - bits 6,5,4,3
			byte exponent = (byte)((highByte % 0x80) / 0x08);

			// get mantisse value - bits 2,1,0 of highByte and all bits of lowByte
			double mantisseDouble = ((highByte % 0x08) * 256) + lowByte;
			double mantisseDoubleDivided = mantisseDouble / Multiplicator;
			
			double signature = 1.0;
			if (negativeNumber)
			{
				mantisseDoubleDivided = FlipMantisse(mantisseDoubleDivided);
				signature = -1.0;
			}

			measurementValue = signature * mantisseDoubleDivided * Math.Pow(2, exponent);

			return measurementValue;
		}

		internal static void Generate_RepType01_Bytes(double measurementValue, out byte lowByte, out byte highByte)
		{
			lowByte = 0;
			highByte = 0;

			CheckMeasurementValueRange(measurementValue);

			bool negativeNumber = (measurementValue < 0);

			// calculate exponent bits
			byte exponent2 = GetExponent(measurementValue);			

			// calculate mantisse
			double powerValue = Math.Pow(2, exponent2);
			double mantisse = measurementValue / powerValue;
			if (negativeNumber)
			{
				mantisse = mantisse * (-1);
				mantisse = FlipMantisse(mantisse);
			}

			double multipliedMantisse = mantisse * Multiplicator;
			uint tmp2 = (uint)(Math.Truncate(multipliedMantisse));
			byte tmp2Low = (byte)(tmp2 % 256);
			byte tmp2High = (byte)((tmp2 / 256) % 0x08);

			// now set lowByte and highByte
			lowByte = tmp2Low;
			highByte = tmp2High;
			highByte += (byte)(exponent2 * 0x08);
			if (negativeNumber)
			{
				highByte += (byte)(0x80);
			}
		}

		#endregion internal methods

		#region private methods

		/// <summary>
		/// Ist x eine negative Zahl, so errechnet sich x in Zweierkomplementdarstellung (x_z) mit n Stellen wie folgt:
		///    x_z = 2^n-|x|
		/// 
		/// Dementsprechend gilt auch
		///    x_z + |x| = 2^n 
		///    wobei |x| der positiven Zahl entspricht und 2^n bei der Rechnung als Übertrag in der (n+1)-sten Stelle auftritt.
		/// </summary>
		private static double FlipMantisse(double mantisse)
		{
			return (double)((Math.Pow(2, 9) - mantisse));
		}

		private static void CheckMeasurementValueRange(double measurementValue)
		{
			if (measurementValue > 670760.96)
			{
				// TODO handle error
				throw new InvalidOperationException("Number too big");
			}

			if (measurementValue < -671088.64)
			{
				// TODO handle error
				throw new InvalidOperationException("Number too small");
			}
		}

		private static byte GetExponent(double measurementValue)
		{
			byte exponent2 = 0x00;
			if ((measurementValue > 511) || (measurementValue < -512))
			{
				double absMeasurementValue = Math.Abs(measurementValue);
				double remValue = absMeasurementValue / 512;
				uint remValueUint = (uint)(Math.Truncate(remValue));
				double log2RemValue = Math.Log(remValueUint, 2);
				uint log2 = (uint)(Math.Truncate(log2RemValue) + 1);
				if (log2 > 0x0f)
				{
					// simply cut off, we only use exponents with max value0x0f
					// TODO handle error
					exponent2 = 0x0f;
				}
				else
				{
					exponent2 = (byte)log2;
				}
			}

			return exponent2;
		}

		#endregion private methods
	}
}
